package org.jeecg.modules.network.network;

import com.zhouwr.common.enums.NetworkType;
import com.zhouwr.common.network.DeviceMessageCodec;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.device.entity.DeviceInstance;
import org.jeecg.modules.device.enums.DeviceInstanceState;
import org.jeecg.modules.device.listener.event.InstanceOfflineEvent;
import org.jeecg.modules.device.listener.event.InstanceOnlineEvent;
import org.jeecg.modules.device.service.IDeviceInstanceService;
import org.jeecg.modules.device.service.IDeviceModelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author zhouwenrong
 */
@Slf4j
@Component
public class NetworkConnectStore {

    private static final Map<String, NetworkConnect> store = new HashMap<>();
    private static NetworkConnectStore networkConnectStore;
    @Autowired
    private IDeviceInstanceService instanceService;
    @Autowired
    private IDeviceModelService modelService;
    @Autowired
    private ApplicationEventPublisher publisher;
    @Autowired
    private NetworkManager networkManager;

    public static Boolean addConnect(ChannelHandlerContext ctx, NetworkType networkType) {

        Network network = networkConnectStore.networkManager.getServiceList().get(ctx.channel().localAddress().toString().substring(1));
        networkConnectStore.resetStore();
        String address = ctx.channel().remoteAddress().toString().substring(1);
        // 1、根据tcp地址查询设备实例，拿到设备实例
        DeviceInstance instance = networkConnectStore.instanceService.getInstanceDeviceByAddress(address);
        if (instance != null && !instance.getStatus().equals(DeviceInstanceState.NOT_ACTIVE)) {
            NetworkConnect connect = new NetworkConnect();

//            addCodec(ctx, instance.getModelBy());
//            connect.setCodec(messageCodec);

            /* 注册时间 */
            connect.setRegistTime(System.currentTimeMillis());
            /* 客户端地址 */
            connect.setSocketAddress(ctx.channel().remoteAddress());
            /* 网络类型 */
            connect.setNetworkType(networkType);
            /* 行为：注册 */
            connect.setAction(1);
            /* 连接通道 */
            connect.setChannelHandlerContext(ctx);
            /* 网络服务 */
            connect.setNetwork(network);
            /* 设备实例状态 */
            instance.setStatus(DeviceInstanceState.ONLINE);
            connect.setInstance(instance);
            /* 2、把连接地址(ip:port)作为map的key写入 */
            store.put(address, connect);
            log.debug("新增连接：{} -> {}，连接数量：{}", connect.getInstance().getName(), address, store.size());
            /* 3、发布事件：更新设备状态 */
            networkConnectStore.publisher.publishEvent(new InstanceOnlineEvent(instance.getSceneBy(), instance.getId()));
            return true;
        } else {
            log.error("新增连接：{}，设备实例不存在或尚未激活！", address);
        }
        return false;
    }

    public static void removeConnect(SocketAddress socketAddress) {
        String address = socketAddress.toString().substring(1);
        NetworkConnect connect = store.get(address);
        if (connect == null) {
            return;
        }
        /* 手动关闭链接 */
        if(connect.getChannelHandlerContext().channel().isActive()) {
            connect.getChannelHandlerContext().channel().close();
        }
        DeviceInstance instance = connect.getInstance();

        store.remove(address);
        log.debug("移除连接：{} <- {}, 连接数量：{}", connect.getInstance().getName(), address, store.size());

        networkConnectStore.publisher.publishEvent(new InstanceOfflineEvent(instance.getSceneBy(), instance.getId()));
    }

    public static void removeConnect(String address) {
        NetworkConnect connect = store.get(address);
        if (connect == null) {
            return;
        }
        /* 手动关闭链接 */
        if(connect.getChannelHandlerContext().channel().isActive()) {
            connect.getChannelHandlerContext().channel().close();
        }
        DeviceInstance instance = connect.getInstance();

        store.remove(address);
        log.debug("移除连接：{} <- {}, 连接数量：{}", connect.getInstance().getName(), address, store.size());

        networkConnectStore.publisher.publishEvent(new InstanceOfflineEvent(instance.getSceneBy(), instance.getId()));
    }

    public static Map<String, NetworkConnect> getNetworkConnectMap() {
        return store;
    }

    private void resetStore() {
        Set<String> set = store.keySet();
        for (String adds : set) {
            NetworkConnect connect = store.get(adds);
            if (connect.getChannelHandlerContext() == null || !connect.getChannelHandlerContext().channel().isRegistered()) {
                store.remove(adds);
            }
        }
    }

    /**
     * 为连接通道添加编解码器
     *
     * @param ctx
     * @param modelId
     */
    private static void addCodec(ChannelHandlerContext ctx, String modelId) {
        DeviceMessageCodec protocol = networkConnectStore.modelService.getProtocol(modelId);
        protocol.getEncoders().forEach((name, channelHandler) -> {
            if (ctx.pipeline().get(channelHandler.getClass()) == null) {
                ctx.pipeline().addLast(name, channelHandler);
            }
        });
        protocol.getDecoders().forEach((name, channelHandler) -> {
            if (ctx.pipeline().get(channelHandler.getClass()) == null) {
                ctx.pipeline().addLast(name, channelHandler);
            }
        });
    }

    private static void removeCodec(ChannelHandlerContext ctx, String modelId) {
        DeviceMessageCodec protocol = networkConnectStore.modelService.getProtocol(modelId);
        protocol.getEncoders().forEach((name, channelHandler) -> {
            if (ctx.pipeline().get(channelHandler.getClass()) == null) {
                ctx.pipeline().addLast(name, channelHandler);
            }
        });
        protocol.getDecoders().forEach((name, channelHandler) -> {
            if (ctx.pipeline().get(channelHandler.getClass()) == null) {
                ctx.pipeline().addLast(name, channelHandler);
            }
        });
    }

    @PostConstruct
    public void init() {
        networkConnectStore = this;
        networkConnectStore.instanceService = this.instanceService;
        networkConnectStore.modelService = this.modelService;
        networkConnectStore.publisher = this.publisher;
        networkConnectStore.networkManager = this.networkManager;
    }
}
